home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 2 / Atari Mega Archive CD - Volume 2.iso / minix / up1510b.tgz / up1510b / src / commands / pretty.c < prev    next >
C/C++ Source or Header  |  1990-07-23  |  11KB  |  504 lines

  1. /* pretty - MINIX prettyprinter        Author: Andy Tanenbaum */
  2.  
  3. /* This program is just a post processor for indent.  Unfortunately,
  4.  * indent can't quite produce MINIX format output.  This program takes
  5.  * the output from indent and fixes up a couple of items:
  6.  *
  7.  *     - the first tab stop is at line_length 3 instead of 9
  8.  *    - short 'if', 'while' and 'for' statements should be on a single line
  9.  *    - placement of comments
  10.  *    - return (value)  ==> return(value)
  11.  *    - etc.
  12.  *
  13.  * To use pretty, install "indent" in /bin or /usr/bin.
  14.  */
  15.  
  16. #include <stdio.h>
  17.  
  18. #define NAME_SIZE          14
  19. #define PATH_MAX          255
  20. #define MAX_LINE           78
  21. #define BUF_SIZE         1024
  22. #define TAB                 8
  23. #define COM_COL            33
  24.  
  25. #define skip_whitespace(p)  while (*p == ' ' || *p == '\t') p++;
  26.  
  27. char buf[BUF_SIZE];
  28. char buf2[BUF_SIZE];
  29. char buf3[BUF_SIZE];
  30. int loops;
  31.  
  32. main(argc, argv)
  33. int argc;
  34. char *argv[];
  35. {
  36.  
  37.   int i, n;
  38.   char *name, intermediate[PATH_MAX+1], *p;
  39.  
  40.   if (argc < 2) usage();
  41.  
  42.   for (i = 1; i < argc; i++) {
  43.     /* Invent a name for the intermediate file. */
  44.     name = argv[i];
  45.     p = name + strlen(name) - 1;
  46.     while (p >= name && *p != '/') p--;
  47.     p++;            /* p points to last component */
  48.     strncpy(intermediate, p, NAME_SIZE);
  49.     if (strlen(intermediate) < NAME_SIZE) {
  50.         /* Name is less than 14 chars.  Just add "+" to name. */
  51.         strcat(intermediate, "+");
  52.     } else {
  53.         /* Name is 14 chars.  Invert case of last character. */
  54.         n = strlen(intermediate);
  55.         intermediate[n - 1] ^= 040;
  56.     }
  57.  
  58.     indent(name, intermediate);
  59.     process(name, intermediate);
  60.     unlink(intermediate);
  61.   }
  62. }
  63.  
  64. indent(s, intermediate)
  65. char *s;            /* name of file to prettyprint */
  66. char *intermediate;        /* name of intermediate file */
  67. {
  68. /* Call indent to indent the file.  Then rename it. */
  69.   int n;
  70.   char buf[BUF_SIZE];
  71.  
  72.   strcpy(buf, "indent ");
  73.   strcat(buf, s);
  74.   strcat(buf," -npro -bap -bbb -br -ncdb -cli0.5 -di1 -lp -npsl -nfc1 -nip\n");
  75.   n = system(buf);
  76.   if (n < 0) {
  77.     fprintf(stderr, "pretty: cannot indent %s\n", s);
  78.     exit(1);
  79.   }
  80.  
  81.   /* Rename the intermediate file. */
  82.   strcpy(buf, "mv ");
  83.   strcat(buf, s);
  84.   strcat(buf, " ");
  85.   strcat(buf, intermediate);
  86.   strcat(buf, "\n");
  87.   n = system(buf);
  88.   if (n < 0) {
  89.     fprintf(stderr, "pretty: mv %s %s failed\n", s, intermediate);
  90.     exit(1);
  91.   }
  92. }
  93.  
  94. process(s, intermediate)
  95. char *s;            /* name of file to prettyprint */
  96. char *intermediate;        /* name of intermediate file */
  97. {
  98. /* File is now indented.  Post process it. */
  99.  
  100.   int t, m, tabcol;
  101.   FILE *in, *out;
  102.   char *p;
  103.  
  104.   in = fopen(intermediate, "r");    /* open intermediate file */
  105.   if (in == NULL) {
  106.     fprintf(stderr, "pretty: cannot open %s\n", intermediate);
  107.     exit(1);
  108.   }
  109.  
  110.   /* Create the output file. */
  111.   out = fopen(s, "w");
  112.   if (out == NULL) {
  113.     fprintf(stderr, "pretty: cannot open %s for writing\n", s);
  114.     exit(1);
  115.   }
  116.  
  117.   /* Process the file a line at a time. */
  118.   loops = 0;
  119.   fgets(buf, BUF_SIZE, in);
  120.   fgets(buf2, BUF_SIZE, in);
  121.   while (1) {
  122.     if (fgets(buf3, BUF_SIZE, in) == NULL) {
  123.         if (buf[0] == '\0') {
  124.             fclose(in);
  125.             fclose(out);
  126.             return;
  127.         } else {
  128.             buf3[0] = '\0';
  129.         }
  130.     }
  131.     do_return(buf3);
  132.  
  133.     /* Lines beginning with \t ==> "  "; \t\t ==> \t */
  134.     if (buf[0] == '\t') {
  135.         /* Check for any subsequent tabs; add 1 if found. */
  136.         p = &buf[0];
  137.         if (buf[1] == '\t') {
  138.             /* Line starts with two tabs. */
  139.             shift_left(buf);
  140.  
  141.             /* If there are more tabs, compensate for lost tab. */
  142.             skip_whitespace(p);
  143.             while (*p != '\t' && *p != '\n') p++;
  144.             if (*p == '\t') {
  145.                 shift_right(p);
  146.                 *p = '\t';    /* insert new tab */
  147.             }
  148.         } else {
  149.             /* Line starts with one tab. */
  150.             shift_right(buf);
  151.             buf[0] = ' ';
  152.             buf[1] = ' ';
  153.         }
  154.     }
  155.     ifwhilefor(in, out);
  156.     do_return(buf);
  157.     do_case(in);
  158.     place_comment(buf);    /* align comment right */
  159.     splice_comment(in);
  160.     three_liner(in);
  161.     capitalize();
  162.     add_blank();
  163.     fputs(buf, out);
  164.     strcpy(buf, buf2);
  165.     strcpy(buf2, buf3);
  166.     loops++;        /* keep track of loop iterations */
  167.   }
  168. }
  169.  
  170.  
  171. join()
  172. {
  173.   char *p, *q;
  174.   int col, line_length();
  175.  
  176.   /* Don't join two if statements. */
  177.   p = buf2;
  178.   skip_whitespace(p);
  179.   if (strncmp(p, "if ", 3) == 0) return(MAX_LINE + 100);
  180.  
  181.   /* Don't join if the third line is 'else'. */
  182.   p = buf3;
  183.   skip_whitespace(p);
  184.   if (strncmp(p, "else", 4) == 0) return(MAX_LINE + 100);
  185.  
  186.   /* Determine how long the joined statements will be. */
  187.   q = buf2;
  188.   skip_whitespace(q);
  189.   col = line_length(buf) + line_length(q) + 1;
  190.   return(col);
  191. }
  192.  
  193.  
  194. shift_left(p)
  195. register char *p;
  196. {
  197. /* Copy a string to the left 1 line_length. */
  198.  
  199.   do {
  200.     *p = *(p + 1);
  201.     p++;
  202.   } while (*p != 0);
  203. }
  204.  
  205. shift_right(p)
  206. register char *p;
  207. {
  208. /* Copy a string to the right 1 line_length. */
  209.  
  210.   register char *q;
  211.  
  212.   q = p + strlen(p);        /* points to 0 byte */
  213.   while (q >= p) {
  214.     *(q + 1) = *q;        /* move character to the right */
  215.     q--;
  216.   }
  217. }
  218.  
  219. int line_length(p)
  220. char *p;
  221. {
  222. /* Determine the length of the line, handling tabs correctly. */
  223.  
  224.   int col = 1;
  225.  
  226.   while (*p != '\n' && *p != '\0') {
  227.     if (*p != '\t') {
  228.         col++;
  229.     } else {
  230.         col += TAB - ((col - 1) % TAB);
  231.     }
  232.     p++;
  233.   }
  234.   return(col - 1);
  235. }
  236.  
  237. place_comment(buf)
  238. char *buf;
  239. {
  240. /* See if there is a comment that has to be readjusted to get it right. */
  241.  
  242.   int col, col2, comcol, last, quotes;
  243.   char tmp[BUF_SIZE];
  244.   char *p, *q, *rb, *rt;
  245.  
  246.   col = 1;
  247.   p = buf;
  248.  
  249.   /* Skip any initial white space in buf. */
  250.   while (*p == ' ' || *p == '\t') {
  251.     if (*p == ' ')
  252.         col++;
  253.     else
  254.         col += TAB - (col - 1) % TAB;
  255.     p++;
  256.   }
  257.   if (*p == '\n') return;    /* empty line */
  258.   if (*p == '/' && *(p + 1) == '*')
  259.     return;            /* line starts with comment */
  260.  
  261.   /* Scan forward looking for a comment. */
  262.   quotes = 0;
  263.   while (1) {
  264.     if (*p == '\n') return;
  265.     if (*p == '"') quotes++;    /* careful about strings */
  266.     if (*p == '/' && *(p + 1) == '*') break;
  267.     if (*p != '\t')
  268.         col++;
  269.     else
  270.         col += TAB - (col - 1) % TAB;
  271.     if (*p != ' ' && *p != '\t') last = col;
  272.     p++;
  273.   }
  274.  
  275.   /* We found a comment.  Where should it go? */
  276.   if (quotes & 1) return;    /* if this is inside a string, return */
  277.   if (last <= COM_COL) {
  278.     comcol = COM_COL;
  279.   } else {
  280.     comcol = ((last - 1) / TAB) * TAB + TAB + 1;
  281.   }
  282.   if (comcol == col) return;    /* it is ok as is */
  283.  
  284.   if (comcol > col) {
  285.     /* Move comment to the right. */
  286.     shift_right(p);
  287.     *p = '\t';
  288.     return;
  289.   }
  290.  
  291.   /* Move comment to the left. */
  292.   q = p - 1;            /* q points to char before comment */
  293.   while (*q == ' ' || *q == '\t') q--;
  294.   rb = buf;
  295.   rt = tmp;
  296.   col2 = 1;
  297.   while (rb <= q) {
  298.     if (*rb != '\t')
  299.         col2++;
  300.     else
  301.         col2 += TAB - (col2 - 1) % TAB;
  302.     *rt++ = *rb++;        /* copy one character */
  303.   }
  304.   while (col2 < comcol) {
  305.     *rt++ = '\t';
  306.     col2 += TAB - (col2 - 1) % TAB;
  307.   }
  308.   *rt = 0;
  309.   strcat(rt, p);
  310.   strcpy(buf, tmp);
  311. }
  312.  
  313.  
  314. ifwhilefor(in, out)
  315. FILE *in, *out;
  316. {
  317. /* Check for 'if' or 'while' split over two lines. */
  318.  
  319.   int is_if, is_while, is_for;
  320.   char *first1, *first2, *second1;
  321.  
  322.   /* Check for 'if', 'while' or 'for' statements split over two lines. */
  323.   first1 = &buf[0];
  324.   skip_whitespace(first1);
  325.   first2 = first1 + strlen(first1) - 2;    /* last char */
  326.   while (first2 > &buf[0] && (*first2 == ' ' || *first2 == '\t')) first2--;
  327.  
  328.   /* First1 and first2 now point to first/last nonblank chars. */
  329.   is_if = (strncmp(first1, "if ", 3) == 0 ? 1 : 0);
  330.   is_for = (strncmp(first1, "for ", 4) == 0 ? 1 : 0);
  331.   is_while = (strncmp(first1, "while ", 6) == 0 ? 1 : 0);
  332.  
  333.   if ((is_if || is_for || is_while) && *first2 == ')') {
  334.     /* This is an 'if' or 'while' statement ending with ')'. */
  335.     if (join() < MAX_LINE) {
  336.         *(first2 + 1) = ' ';
  337.         *(first2 + 2) = 0;
  338.         second1 = &buf2[0];
  339.         while (*second1 == ' ' || *second1 == '\t') second1++;
  340.         strcat(first2, second1);
  341.         scrunch(in);
  342.     }
  343.   }
  344. }
  345.  
  346. do_return(b)
  347. char *b;
  348. {
  349. /* Remove the space after return, i.e., return (0) ==> return(0). */
  350.  
  351.   char *p;
  352.  
  353.   p = b;
  354.   skip_whitespace(p);
  355.   if (strncmp(p, "return (", 8) != 0) return;
  356.   shift_left(p + 6);
  357. }
  358.  
  359.  
  360. splice_comment(in)
  361. FILE *in;
  362. {
  363. /* Indent has the problem that it sometimes breaks one line comments over two
  364.  * lines, even though the original comment fits quite well on one line.  Fix.
  365.  */
  366.  
  367.   char *p, *q;
  368.  
  369.   p = buf2;            /* affected lines are followed by * something */
  370.   skip_whitespace(p);
  371.   if (*p != '*' || *(p + 1) != ' ') return;
  372.  
  373.   /* Second line starts with * something.  See